package com.tool.emulator.apple2;

import com.tool.emulator.apple2.pers.DiskII;
import com.tool.emulator.apple2.pers.VirtualPrinter;
import com.tool.emulator.common.AccessUnit;
import com.tool.emulator.common.Emulator;
import com.tool.emulator.mos6502.BUS6502;
import com.tool.emulator.mos6502.CPU6502;

import java.io.*;

/**
 * 一个apple2 运行时虚拟机对象
 * Apple.java @author tl 2011-6-18
 */
public class Apple2 implements Apple2Constants {
	public static volatile boolean APPLEIIE = false;
	public final BUS6502 BUS = new BUS6502();
	public final CPU6502 CPU = BUS.getCPU();
	public final AccessUnit[] MEM = BUS.getMemory();
	public final UnsupportedIO IODUMPER;
	public final Monitor MONITOR;
	public final Keyboard KEYBOARD;
	public final Speaker SPEAKER;
	public final Recorder RECORDER;
	public final DiskII DISK;
	public final VirtualPrinter PRINTER;
	public volatile boolean RECORDER_OUT_TO_SPEAKER = true;
	public volatile boolean RECORDER_IN_TO_SPEAKER = true;

	public Apple2() throws Exception {
		this(Apple2.APPLEIIE ? "Apple2E2.ROM" : "apple.rom");
	}

	public Apple2(String romFile) throws Exception {
		BUS.initRam(RAM_OFFSET, RAM_SIZE);
		BUS.initUnuse(Apple2.IO_OFFSET, 0x1000);
		this.loadRom(Apple2.class.getResourceAsStream(romFile));

		IODUMPER = new UnsupportedIO(this);
		MONITOR = new Monitor(this);
		KEYBOARD = new Keyboard(this);
		SPEAKER = new Speaker(this);
		RECORDER = new Recorder(this);
		DISK = new DiskII(this);
		PRINTER = new VirtualPrinter(this);
		BUS.getEmulator().addDevice(this.IODUMPER);
		BUS.getEmulator().addDevice(this.MONITOR);
		BUS.getEmulator().addDevice(this.KEYBOARD);
		BUS.getEmulator().addDevice(this.SPEAKER);
		BUS.getEmulator().addDevice(this.RECORDER);
		BUS.getEmulator().addDevice(this.DISK);
		BUS.getEmulator().addDevice(this.PRINTER);
		PRINTER.install();
	}

	public byte[] dump() throws Exception {
		ByteArrayOutputStream bout = new ByteArrayOutputStream();
		DataOutputStream out = new DataOutputStream(bout);
		this.dump(out);
		out.close();
		return bout.toByteArray();
	}

	public void dumpToFile(File file) throws Exception {
		DataOutputStream out = Util.saveFile(file, "A2DUMP");
		try {
			this.dump(out);
		} finally {
			out.close();
		}
	}

	public void dump(OutputStream out) throws Exception {
		this.getEmulator().saveState(out);
	}

	public void restoreFromFile(File file) throws Exception {
		DataInputStream in = Util.openFile(file, "A2DUMP");
		try {
			restore(new DataInputStream(new BufferedInputStream(in)));
		} finally {
			in.close();
		}
	}

	public void restore(byte[] data) throws Exception {
		restore(new DataInputStream(new ByteArrayInputStream(data)));
	}

	public void restore(DataInputStream in) throws Exception {
		this.getEmulator().loadState(in);
	}

	public void loadRom(InputStream in) throws Exception {
		byte[] data;
		try {
			ByteArrayOutputStream bout = new ByteArrayOutputStream();
			byte[] buf = new byte[1024];
			for (; ; ) {
				int l = in.read(buf);
				if (l < 0)
					break;
				bout.write(buf, 0, l);
			}
			data = bout.toByteArray();
		} finally {
			in.close();
		}
		this.BUS.initRom(0x10000 - data.length, data);
	}

	public void loadRom(File file) throws Exception {
		loadRom(new FileInputStream(file));
	}

	public void start() throws Exception {
		this.getEmulator().start();
	}

	public void shutdown() throws Exception {
		this.getEmulator().shutdown();
	}

	public void reset() throws Exception {
		this.getEmulator().pause(true);
		for (int i = 0; i < Apple2.RAM_SIZE; i++)
			MEM[i].directSet(0);
		this.getEmulator().restart();
	}

	public Emulator getEmulator() {
		return BUS.getEmulator();
	}

}
